package JMH.day03.atomicreference;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.atomic.AtomicStampedReference;

public class DebitCard {

    private final String account;
    private final int money;

    public DebitCard(String account, int money) {
        this.account = account;
        this.money = money;
    }

    public String getAccount() {
        return account;
    }

    public int getMoney() {
        return money;
    }

    @Override
    public String toString() {
        return "DebitCard{" +
                "account='" + account + '\'' +
                ", money=" + money +
                '}';
    }
}

/**
 * 会出现线程安全问题
 */
class Go {

    static volatile DebitCard card = new DebitCard("Mixin", 1000);

    public static void main(String[] args) {

        for (int i = 0; i < 10; i++) {
            new Thread(() -> {
                while (true) {
                    final DebitCard debitCard = card;
                    DebitCard newCard = new DebitCard(debitCard.getAccount(), debitCard.getMoney() + 10);
                    System.out.println(Thread.currentThread().getName() + ":" + newCard);
                    card = newCard;
                    try {
                        TimeUnit.MILLISECONDS.sleep(20);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }).start();
        }

    }

}

/**
 * 基于AtomicReference进行非阻塞多线程优化，保证更新正确
 */
class GoReference {

    private static AtomicReference<DebitCard> card = new AtomicReference<>(new DebitCard("Mixin", 1000));

    public static void main(String[] args) {

        for (int i = 0; i < 10; i++) {
            new Thread(() -> {
                while (true) {
                    final DebitCard debitCard = card.get();
                    DebitCard newCard = new DebitCard(debitCard.getAccount(), debitCard.getMoney() + 10);
                    // 基于CAS算法进行更新操作
                    if (card.compareAndSet(debitCard, newCard)) {
                        System.out.println(Thread.currentThread().getName() + ":" + newCard);
                    }
                    try {
                        TimeUnit.MILLISECONDS.sleep(20);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }).start();
        }

    }
}

class GoStampedReference {

    private static AtomicStampedReference<DebitCard> card =
            new AtomicStampedReference<>(new DebitCard("Mixin", 100), 1);

    public static void main(String[] args) {

        for (int i = 0; i < 10; i++) {
            new Thread(() -> {
                while (true) {
                    final DebitCard debitCard = card.getReference();
                    DebitCard newCard = new DebitCard(debitCard.getAccount(), debitCard.getMoney() + 10);
                    int expectedStamp = card.getStamp();
                    int newStamp = expectedStamp + 1;
                    if (card.compareAndSet(debitCard, newCard, expectedStamp, newStamp)) {
                        System.out.println(Thread.currentThread().getName() + ":" + card.getReference());
                    }
                    try {
                        TimeUnit.MILLISECONDS.sleep(20);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }).start();
        }

    }
}